home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
byte1087.arc
/
OS2AP.ARC
/
DUMP.ASM
next >
Wrap
Assembly Source File
|
1987-09-14
|
26KB
|
647 lines
DUMP.ASM
name dump
page 55,132
title DUMP --- Display File Contents
.286c
;
; DUMP.ASM --- a OS/2 utility to display the contents of a
; file on the standard output in hex and ASCII format.
;
; Copyright (C) 1987 Ray Duncan
;
; Usage: C>DUMP path\filename.ext [ >device ]
;
; This program has been intentionally complicated
; to demonstrate the use of multiple threads and semaphores
; in a MASM application. For a roadmap to what is going
; on in this program, see its counterpart DUMP.C.
;
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII line feed
blank equ 20h ; ASCII space code
tab equ 09h ; ASCII tab code
recsize equ 16 ; size of input file records
stksize equ 2048 ; size of stack for threads
stdout equ 1 ; handle of standard output device
stderr equ 2 ; handle of standard error device
extrn DOSOPEN:far ; references to OS/2 services
extrn DOSREAD:far
extrn DOSWRITE:far
extrn DOSCLOSE:far
extrn DOSEXIT:far
extrn DOSSEMCLEAR:far
extrn DOSSEMSET:far
extrn DOSSEMWAIT:far
extrn DOSALLOCSEG:far
extrn DOSCREATETHREAD:far
extrn DOSSUSPENDTHREAD:far
extrn DOSENTERCRITSEC:far
extrn DOSEXITCRITSEC:far
extrn DOSGETENV:far
DGROUP group _DATA
_DATA segment word public 'DATA'
ExitSem dd 0 ; storage for RAM semaphores
Buf1FullSem dd 0
Buf1EmptySem dd 0
Buf2FullSem dd 0
Buf2EmptySem dd 0
DisplayThrID dw 0 ; Display thread ID
DiskThrID dw 0 ; Disk I/O thread ID
Buf1 db recsize dup (0) ; disk I/O buffer #1
Buf1Len dw 0 ; length of buffer #1 data
Buf2 db recsize dup (0) ; disk I/O buffer #2
Buf2Len dw 0 ; length of buffer #2 data
fname db 64 dup (0) ; ASCIIZ name of input file
fhandle dw 0 ; handle for input file
filptr dw 0 ; relative address in file
status dw 0 ; receives status of DOSOPEN
selector dw 0 ; receives segment selector
; from DOSALLOCSEG
; formatting area for output
output db 'nnnn',blank,blank
outputa db 16 dup ('nn',blank)
db blank
outputb db 16 dup (blank),cr,lf
output_len equ $-output
heading db cr,lf ; heading for each 128 bytes
db 7 dup (blank)
db '0 1 2 3 4 5 6 7 '
db '8 9 A B C D E F',cr,lf
heading_len equ $-heading
msg1 db cr,lf
db 'dump: file not found'
db cr,lf
msg1_len equ $-msg1
msg2 db cr,lf
db 'dump: missing file name'
db cr,lf
msg2_len equ $-msg2
msg3 db cr,lf
db 'dump: memory allocation error'
db cr,lf
msg3_len equ $-msg3
msg4 db cr,lf
db 'dump: create thread failed'
db cr,lf
msg4_len equ $-msg4
_DATA ends
_TEXT segment word public 'CODE'
assume cs:_TEXT,ds:DGROUP
dump proc far ; entry point from OS/2
call argc ; is filename present?
cmp ax,2
je dump1 ; yes, proceed
mov dx,offset msg2 ; missing or illegal filespec,
mov cx,msg2_len
jmp dump9 ; print error message and exit.
dump1: ; copy filename to local buffer
mov ax,1 ; get ES:BX = filename
call argv
mov cx,ax ; set CX = length
mov di,offset fname ; DS:DI = local buffer
dump15: mov al,es:[bx] ; copy it byte by byte
mov [di],al
inc bx
inc di
loop dump15
push ds ; set ES = DS
pop es
dump2: ; now try to open file...
push ds ; ASCIIZ file name
push offset DGROUP:fname
push ds ; receives handle
push offset DGROUP:fhandle
push ds ; receives handle
push offset DGROUP:status
push 0 ; file size (ignored)
push 0
push 0 ; file attribute = normal
push 1 ; OpenFlag = fail if doesn't exist
push 40h ; OpenMode = deny none,read only
push 0 ; DWORD reserved
push 0
call DOSOPEN ; transfer to OS/2
or ax,ax ; test status
jz dump3 ; jump if open succeeded
mov dx,offset msg1 ; open of input file failed,
mov cx,msg1_len
jmp dump9 ; print error msg and exit.
dump3: ; initialize semaphores
push ds
push offset DGROUP:ExitSem
call DOSSEMSET
push ds
push offset DGROUP:Buf1FullSem
call DOSSEMSET
push ds
push offset DGROUP:Buf2FullSem
call DOSSEMSET
; allocate Disk Thread stack
push stksize ; size of stack
push ds ; receives selector for
push offset DGROUP:selector ; allocated block
push 0 ; 0 = segment not shareable
call DOSALLOCSEG ; transfer to OS/2
or ax,ax ; test status
jz dump5 ; jump if allocation succeeded
dump4: mov dx,offset DGROUP:msg3 ; display message
mov cx,msg3_len ; 'memory allocation error'
jmp dump9 ; and exit
dump5: ; create Disk Thread
push cs ; thread's entry point
push offset _TEXT:DiskThread
push ds ; receives thread ID
push offset DGROUP:DiskThrID
push selector ; thread's stack base
push stksize
call DOSCREATETHREAD ; transfer to OS/2
or ax,ax ; test status
jz dump7 ; jump if create succeeded
dump6: mov dx,offset DGROUP:msg4 ; create of thread failed,
mov cx,msg4_len ; display error message
jmp dump9 ; and exit
dump7: ; allocate Display Thread stack
push stksize ; size of stack
push ds ; receives selector for
push offset DGROUP:selector ; allocated block
push 0 ; 0 = segment not shareable
call DOSALLOCSEG ; transfer to OS/2
or ax,ax ; test status
jnz dump4 ; jump if allocation failed
; create Display Thread
push cs ; thread's entry point
push offset _TEXT:DisplayThread
push ds ; receives thread ID
push offset DGROUP:DisplayThrID
push selector ; thread's stack base
push stksize
call DOSCREATETHREAD ; transfer to OS/2
or ax,ax ; test status
jnz dump6 ; jump if create failed
push ds ; now wait on exit semaphore
push offset DGROUP:ExitSem ; (it will be triggered
push -1 ; by routine DumpRec when
push -1 ; end of file is reached)
call DOSSEMWAIT ; transfer to OS/2
push DiskThrID ; suspend Disk Thread
call DOSSUSPENDTHREAD ; transfer to OS/2
push DisplayThrID ; suspend Display Thread
call DOSSUSPENDTHREAD ; transfer to OS/2
push fhandle ; close the input file
call DOSCLOSE ; transfer to OS/2
push 1 ; terminate all threads
push 0 ; return code 0 for success
call DOSEXIT ; final exit to OS/2
dump9: ; print error message...
push stderr ; standard error device handle
push ds ; address of message
push dx
push cx ; length of message
push ds ; receives bytes written
push offset DGROUP:status
call DOSWRITE ; transfer to OS/2
push 1 ; terminate all threads
push 1 ; return code <>0 for error
call DOSEXIT ; final exit to OS/2
dump endp
DiskThread proc far ; this thread performs
; the file I/O, alternating
; between the two buffers
; fill buffer #1
push fhandle ; handle for input file
push ds ; address of buffer #1
push offset DGROUP:Buf1
push recsize ; record length requested
push ds ; receives bytes read
push offset DGROUP: Buf1Len
call DOSREAD
; signal buffer 1 has data
mov si,offset DGROUP:Buf1EmptySem
mov di,offset DGROUP:Buf1FullSem
call SemFlip
push ds ; wait until buffer 2 empty
push offset DGROUP:Buf2EmptySem
push -1
push -1
call DOSSEMWAIT
; fill buffer #2
push fhandle ; handle for input file
push ds ; address of buffer #1
push offset DGROUP:Buf2
push recsize ; record length requested
push ds ; receives bytes read
push offset DGROUP:Buf2Len
call DOSREAD
; signal buffer 2 has data
mov si,offset DGROUP:Buf2EmptySem
mov di,offset DGROUP:Buf2FullSem
call SemFlip
push ds ; wait until buffer 1 empty
push offset DGROUP:Buf1EmptySem
push -1
push -1
call DOSSEMWAIT
jmp DiskThread ; do it again...
DiskThread endp
DisplayThread proc far ; formats and displays disk
; data, alternating between
; the two disk buffers
push ds ; wait until buffer #1 full
push offset DGROUP:Buf1FullSem
push -1
push -1
call DOSSEMWAIT
mov si,offset DGROUP:Buf1 ; display buffer 1
mov cx,Buf1Len
call DumpRec
; signal buffer #1 is emptied
mov si,offset DGROUP:Buf1FullSem
mov di,offset DGROUP:Buf1EmptySem
call SemFlip
push ds ; wait until buffer #2 full
push offset DGROUP:Buf2FullSem
push -1
push -1
call DOSSEMWAIT
mov si,offset DGROUP:Buf2 ; display buffer 2
mov cx,Buf2Len
call DumpRec
; signal buffer #2 is emptied
mov si,offset DGROUP:Buf2FullSem
mov di,offset DGROUP:Buf2EmptySem
call SemFlip
jmp DisplayThread ; do it again...
DisplayThread endp
SemFlip proc near ; Flip status of two
; semaphores atomically
call DOSENTERCRITSEC ; protect this code sequence
push ds ; set semaphore #1
push si
call DOSSEMSET
push ds ; clear semaphore #2
push di
call DOSSEMCLEAR
call DOSEXITCRITSEC ; let other threads run again
ret
SemFlip endp
DumpRec proc near ; formats and displays
; contents of buffer
; DS:SI = buffer, CX = length
or cx,cx ; anything to format?
jnz DumpRec1 ; yes, continue
push ds ; no, clear exit semaphore
push offset DGROUP:ExitSem ; (releasing wait condition
call DOSSEMCLEAR ; for main thread)
push 0 ; and terminate this thread
push 0
call DOSEXIT
DumpRec1: ; time for a heading?
test filptr,07fh ; if 128 byte boundary
jnz DumpRec2 ; no,jump
push stdout ; standard output device handle
push ds ; address of heading text
push offset DGROUP:heading
push heading_len ; length of heading
push ds ; receives bytes written
push offset DGROUP:status
call DOSWRITE
DumpRec2: ; format record data...
push cx ; save record length
mov di,offset output ; first clear output area
mov cx,output_len-2
mov al,blank
rep stosb
mov di,offset output ; convert current file offset
mov ax,filptr ; to ASCII for output
call w2hex
pop cx ; get back record length
mov bx,0 ; initialize record pointer
DumpRec3: ; fetch next byte from buffer
mov al,[si+bx]
; store ASCII version of character
mov di,offset outputb ; calculate output string address
mov byte ptr [di+bx],'.' ; if not alphanumeric
cmp al,blank ; just print a dot.
jb DumpRec4 ; jump, not alphanumeric.
cmp al,7eh
ja DumpRec4 ; jump, not alphanumeric.
mov [di+bx],al ; else store ASCII character.
DumpRec4: ; now convert binary byte
; to hex ASCII equivalent
mov di,offset outputa ; calc. position in output string
add di,bx ; base addr + (offset*3)
add di,bx
add di,bx ; convert data in AL to hex
call b2hex ; ASCII and store into output
inc bx ; bump data pointer and loop
loop DumpRec3 ; until entire record converted
; now display formatted data
push stdout ; standard output device handle
push ds ; address of text
push offset DGROUP:output
push output_len ; length of text
push ds
push offset DGROUP:status ; receives bytes written
call DOSWRITE
add word ptr filptr,recsize ; update file pointer
ret ; return to caller
DumpRec endp
argc proc near ; count command line arguments
; returns count in AX
enter 4,0 ; make room for local variables
; and give them names...
envseg equ [bp-2] ; environment segment
cmdoffs equ [bp-4] ; command line offset
push es ; save original ES,BX, and CX
push bx
push cx
push ss ; get selector for environment
lea ax,envseg ; and offset of command line
push ax
push ss
lea ax,cmdoffs
push ax
call DOSGETENV ; transfer to OS/2
or ax,ax ; check operation status
mov ax,1 ; force argc >= 1
jnz argc3 ; inexplicable failure
mov es,envseg ; set ES:BX = command line
mov bx,cmdoffs
argc0: inc bx ; ignore useless first field
cmp byte ptr es:[bx],0
jne argc0
argc1: mov cx,-1 ; set flag = outside argument
argc2: inc bx ; point to next character
cmp byte ptr es:[bx],0
je argc3 ; exit if null byte
cmp byte ptr es:[bx],blank
je argc1 ; outside argument if ASCII blank
cmp byte ptr es:[bx],tab
je argc1 ; outside argument if ASCII tab
; otherwise not blank or tab,
jcxz argc2 ; jump if already inside argument
inc ax ; else found argument, count it
not cx ; set flag = inside argument
jmp argc2 ; and look at next character
argc3: pop cx ; restore original BX, CX, ES
pop bx
pop es
leave ; discard local variables
ret ; return AX = argument count
argc endp
argv proc near ; get address and length
; of command line arguments
; call with AX = arg. no.
; return ES:BX = address of
; argument string, CX = length
enter 4,0 ; make room for local variables
push cx ; save original CX and DI
push di
push ax ; save argument number
push ss ; get selector for environment
lea ax,envseg ; and offset of command line
push ax
push ss
lea ax,cmdoffs
push ax
call DOSGETENV ; transfer to OS/2
or ax,ax ; test operation status
pop ax ; restore argument number
jnz argv7 ; jump if DOSGETENV failed
mov es,envseg ; set ES:BX = command line
mov bx,cmdoffs
or ax,ax ; is requested argument=0?
jz argv8 ; yes, jump to get program name
argv0: inc bx ; scan off first field
cmp byte ptr es:[bx],0
jne argv0
xor ah,ah ; initialize argument counter
argv1: mov cx,-1 ; set flag = outside argument
argv2: inc bx ; point to next character
cmp byte ptr es:[bx],0
je argv7 ; exit if null byte
cmp byte ptr es:[bx],blank
je argv1 ; outside argument if ASCII blank
cmp byte ptr es:[bx],tab
je argv1 ; outside argument if ASCII tab
; if not blank or tab...
jcxz argv2 ; jump if already inside argument
inc ah ; else count arguments found
cmp ah,al ; is this the one we need?
je argv4 ; yes, go find its length
not cx ; no, set flag = inside argument
jmp argv2 ; and look at next character
argv4: ; found desired argument, now
; determine its length...
mov ax,bx ; save param. starting address
argv5: inc bx ; point to next character
cmp byte ptr es:[bx],0
je argv6 ; found end if null byte
cmp byte ptr es:[bx],blank
je argv6 ; found end if ASCII blank
cmp byte ptr es:[bx],tab
jne argv5 ; found end if ASCII tab
argv6: xchg bx,ax ; set ES:BX = argument address
sub ax,bx ; and AX = argument length
jmp argvx ; return to caller
argv7: xor ax,ax ; set AX = 0, argument not found
jmp argvx ; return to caller
argv8: ; special handling for argv=0
xor di,di ; find the program name by
xor al,al ; first skipping over all the
mov cx,-1 ; environment variables...
cld
argv9: repne scasb ; scan for double null (can't use
scasb ; (SCASW since might be odd addr.)
jne argv9 ; loop if it was a single null
mov bx,di ; save program name address
mov cx,-1 ; now find its length...
repne scasb ; scan for another null byte
not cx ; convert CX to length
dec cx
mov ax,cx ; return length in AX
argvx: ; common exit point
pop di ; restore original CX and DI
pop cx
leave ; discard stack frame
ret ; return to caller
argv endp
w2hex proc near ; convert word to hex ASCII
; call with AX=binary value
; DI=addr to store string
; returns AX, DI destroyed
push ax
mov al,ah
call b2hex ; convert upper byte
pop ax
call b2hex ; convert lower byte
ret ; back to caller
w2hex endp
b2hex proc near ; convert byte to hex ASCII
; call with AL=binary value
; DI=addr to store string
; returns AX, DI destroyed
push cx ; save CX for later
sub ah,ah ; clear upper byte
mov cl,16
div cl ; divide binary data by 16
call ascii ; the quotient becomes the first
stosb ; ASCII character
mov al,ah
call ascii ; the remainder becomes the
stosb ; second ASCII character
pop cx ; restore contents of CX
ret
b2hex endp
ascii proc near ; convert value 0-0FH in AL
; into a "hex ASCII" character
add al,'0'
cmp al,'9'
jle ascii2 ; jump if in range 0-9,
add al,'A'-'9'-1 ; offset it to range A-F,
ascii2: ret ; return ASCII char. in AL.
ascii endp
_TEXT ends
end dump